using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using WeiBeeCommon.DataTypes;
using System.Linq;
using System.IO;
using System.Xml.Serialization;
using System.Text;
using counts = System.Collections.Generic.List<WeiBeeCommon.DataTypes.count>;

namespace WeiBeeCommon.Core
{
    public class StatusAlert {
        public String ScreenName;
        public String Name;
        public String Text;
    }

    public class Result {
        public String ID;
        public String Text;
        public String ProfileImageUrl;
        public String DateUpdated;
        public String CreatedAt;
    }

    public class WeiBeeSina : IWeiBee
    {
        private const string WeibeesinaUrl = "http://api.t.sina.com.cn/";
        private const string PathVerify = "account/verify_credentials";
        private const string PathFriendsTimeline = "statuses/friends_timeline";
        private const string PathStatusUpdate = "statuses/update";

        public OAuthSina OAuth{ get; set; }

        public bool IsEnabled
        {
            get 
            {
                return !string.IsNullOrEmpty(OAuth.Token); 
            }
        }

        public WeiBeeSina()
        {
            if( OAuth==null )
            {
                OAuth = new OAuthSina();
            }
        }

        /// <summary> Get a specified user's details </summary>
        public static Result GetUserInfo() {
            OAuthSina oAuthTwitter = CreateOAuthTwitterObject();
            string xml = oAuthTwitter.OAuthWebRequest(Method.GET,
                                                      WeibeesinaUrl + PathVerify + ".xml",
                                                      string.Empty);

            XmlDocument xmldoc = new XmlDocument();
            xmldoc.LoadXml(xml);
            return GetUserInfoFromNode(xmldoc.DocumentElement);
        }



        //--------------------------------------------------------------
        // Private Methods
        //--------------------------------------------------------------
        #region PrivateMethods
        private static OAuthSina CreateOAuthTwitterObject() {
            OAuthSina oAuthTwitter = new OAuthSina();

            return oAuthTwitter;
        }

        private static List<Result> GetStatusList(XmlDocument xml) {
            var statusList = new List<Result>();

            foreach (XmlNode statusNode in xml.GetElementsByTagName("status")) {
                var statusInfo = new Result();
                string statusText = WebHelper.UrlDecode(statusNode["text"].InnerText);
                statusInfo.Text = statusText;
                statusInfo.ID = statusNode["id"].InnerText;
                statusInfo.CreatedAt = ConvertTwitterDate(statusNode["created_at"].InnerText);

                var UserInfo = GetUserInfoFromNode(statusNode.SelectSingleNode("user"));
                statusInfo.ProfileImageUrl = UserInfo.ProfileImageUrl;

                statusList.Add(statusInfo);
            }

            return statusList;
        }

        /// <summary> Parse twitter date into user friendly display date/time. </summary>
        /// <param name="TwitterDate">DateTime as returned by twitter. e.g. Sun Dec 20 15:16:16 +0000 2009</param>
        private static string ConvertTwitterDate(string TwitterDate) {
            string[] Elements = TwitterDate.Split(' ');

            string DayElement = Convert.ToInt32(Elements[2]) == DateTime.Now.Day ? "Today" : Elements[0];
            
            string TimeElement = Elements[3];
            TimeElement = TimeElement.Substring(0, TimeElement.LastIndexOf(':'));

            return string.Concat(DayElement, " ", TimeElement);
        }

        private static Result GetUserInfoFromNode(XmlNode UserNode) {
            Result UserInfo = new Result();

            // Get the user's latest status
            XmlNode UserStatusNode = UserNode.SelectSingleNode("status");

            // This info may not exist on all user nodes
            if (UserStatusNode != null)
                UserInfo.Text = UserStatusNode["text"].InnerText;

            UserInfo.ProfileImageUrl = UserNode["profile_image_url"].InnerText;

            return UserInfo;
        }
        #endregion

        
        /// <summary>
        /// 发布一条微博信�?
        /// </summary>
        /// <param name="sMessage">文字内容</param>
        /// <returns>未知</returns>
        public string AddTwitter(string sMessage)
        {
            return OAuth.OAuthWebRequest(Method.POST,
                                                      WeibeesinaUrl + PathStatusUpdate + ".xml",
                                                      "status=" + Utility.UrlEncode(sMessage));
        }

        /// <summary>
        /// 发布一条图片微博信�?
        /// </summary>
        /// <param name="sMessage">文字内容</param>
        /// <param name="pictureFilename">图片文件路径</param>
        /// <returns>未知</returns>
        public string UpdateStatus(string sMessage, string pictureFilename)
        {
            string url = @"http://api.t.sina.com.cn/statuses/upload.xml";
            return OAuth.OAuthWebRequest(Method.POST,
                                                      url,
                                                      "status=" + sMessage,
                                                      pictureFilename);
        }

        /// <summary>
        /// 根据ID评论一条微�?
        /// </summary>
        /// <param name="id">微博ID</param>
        /// <param name="comment">评论</param>
        /// <returns></returns>
        public string Comment(string id, string comment)
        {
            string url = @"http://api.t.sina.com.cn/statuses/comment.xml";
            string para = string.Format("id={0}&comment={1}", id, comment);
            return OAuth.OAuthWebRequest(Method.POST, url, para);
        }

        /// <summary>
        /// 根据ID转发一条微博
        /// </summary>
        /// <param name="id">微博ID</param>
        /// <param name="status">转发理由</param>
        /// <returns></returns>
        public string Retweet(string id, string status)
        {
            string url = @"http://api.t.sina.com.cn/statuses/repost.xml";
            string para = string.Format("id={0}&status={1}", id, status);
            return OAuth.OAuthWebRequest(Method.POST, url, para);
        }

        /// <summary> Get friends timeline </summary>
        public List<Result> GetFriendTimelines()
        {
            //string NumberOfTweets = "count=50";
            //OAuthSina oAuthTwitter = CreateOAuthTwitterObject();
            string xml = OAuth.OAuthWebRequest(Method.GET,
                                                      WeibeesinaUrl + PathFriendsTimeline + ".xml",
                                                      string.Empty); //NumberOfTweets);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);
            return GetStatusList(xmlDoc);
        }

        public List<SinaStatus> GetPublicTimelines(int count=20)
        {
            string url = @"http://api.t.sina.com.cn/statuses/public_timeline.xml";
            string xml = OAuth.OAuthWebRequest(Method.GET, string.Format("{0}?count={1}", url, count), string.Empty);
            if (xml.StartsWith("Error", true, System.Globalization.CultureInfo.CurrentCulture)) return null;
            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);
            XmlNodeList nodeList = xmlDoc.SelectNodes("/statuses/status");
            var statusList = GetStatusList(nodeList);
            return statusList;
        }

        /// <summary>
        /// Get rate limit result string
        /// </summary>
        /// <returns></returns>
        public string RateLimitStatus()
        {
            string url = @"http://api.t.sina.com.cn/account/rate_limit_status.xml";
            string xml = OAuth.OAuthWebRequest(Method.GET, url, null);
            return xml;
        }

        public List<SinaStatus> GetStatusList(XmlNodeList nodeList)
        {
            var statusList = new List<SinaStatus>();
            foreach (XmlNode node in nodeList)
            {
                var status = new SinaStatus();
                status.created_at = node.SelectSingleNode("created_at").InnerText; 
                status.id = node.SelectSingleNode("id").InnerText;
                status.text = node.SelectSingleNode("text").InnerText;
                //status.source = node.SelectSingleNode("source").InnerText;
                status.favorited = bool.Parse(node.SelectSingleNode("favorited").InnerText);
                if (string.IsNullOrEmpty(node.SelectSingleNode("truncated").InnerText))
                {
                    status.truncated = false;
                }
                else
                {
                    status.truncated = bool.Parse(node.SelectSingleNode("truncated").InnerText);
                }
                status.in_reply_to_status_id = node.SelectSingleNode("in_reply_to_status_id").InnerText;
                status.in_reply_to_user_id = node.SelectSingleNode("in_reply_to_user_id").InnerText;
                status.in_reply_to_screen_name = node.SelectSingleNode("in_reply_to_screen_name").InnerText;
                if (node.SelectSingleNode("thumbnail_pic") != null)
                {
                    status.thumbnail_pic = node.SelectSingleNode("thumbnail_pic").InnerText;
                    status.bmiddle_pic = node.SelectSingleNode("bmiddle_pic").InnerText;
                    status.original_pic = node.SelectSingleNode("original_pic").InnerText;
                }
                status.mid = node.SelectSingleNode("mid").InnerText;
                status.user = GetSinaUser(node.SelectSingleNode("user"));
                statusList.Add(status);
            }
            return statusList;

        }

        private SinaUser GetSinaUser(XmlNode xmlNode)
        {
            var usr = new SinaUser();
            usr.id = xmlNode.SelectSingleNode("id").InnerText;
            usr.screen_name = xmlNode.SelectSingleNode("screen_name").InnerText;
            usr.name = xmlNode.SelectSingleNode("name").InnerText; 
            usr.province = int.Parse(xmlNode.SelectSingleNode("province").InnerText);
            usr.city = int.Parse(xmlNode.SelectSingleNode("city").InnerText);
            usr.location = xmlNode.SelectSingleNode("location").InnerText;
            usr.description = xmlNode.SelectSingleNode("description").InnerText;
            usr.url = xmlNode.SelectSingleNode("url").InnerText;
            usr.profile_image_url = xmlNode.SelectSingleNode("profile_image_url").InnerText;
            usr.domain = xmlNode.SelectSingleNode("domain").InnerText;
            usr.gender = xmlNode.SelectSingleNode("gender").InnerText;
            usr.followers_count = int.Parse(xmlNode.SelectSingleNode("followers_count").InnerText);
            usr.friends_count = int.Parse(xmlNode.SelectSingleNode("friends_count").InnerText);
            usr.statuses_count = int.Parse(xmlNode.SelectSingleNode("statuses_count").InnerText);
            usr.favourites_count = int.Parse(xmlNode.SelectSingleNode("favourites_count").InnerText);
            usr.created_at = xmlNode.SelectSingleNode("created_at").InnerText;
            usr.following = bool.Parse(xmlNode.SelectSingleNode("following").InnerText);
            usr.verified = bool.Parse(xmlNode.SelectSingleNode("verified").InnerText);
            usr.allow_all_act_msg = bool.Parse(xmlNode.SelectSingleNode("allow_all_act_msg").InnerText);
            usr.geo_enabled = bool.Parse(xmlNode.SelectSingleNode("geo_enabled").InnerText);
            return usr;
        }

        public void SetOAuth(string token, string secret)
        {
            OAuth.Token = token;
            OAuth.TokenSecret = secret;
        }

        public string AddPicture(string text, string picture)
        {
            return UpdateStatus(text,picture);
        }

        public string GetUserId()
        {
            return this.GetWeiBeeUser().id;
        }

        /// <summary>
        /// Batch get the comment and retweet counts from the tweets
        /// </summary>
        /// <param name="status">tweets</param>
        /// <returns>counts</returns>
        public List<count> GetCounts(List<SinaStatus> status)
        {
            List<count> counts = null;
            if( (status !=null ) && (status.Count > 0) )
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(status[0].id);
                for (int i = 1; i < status.Count && i < 100; i++)
                {
                    sb.Append("," + status[i].id);
                }
                string url = @"http://api.t.sina.com.cn/statuses/counts.xml";
                string para = string.Format("{0}?ids={1}", url, Utility.UrlEncode(sb.ToString()));
                try
                {
                    string result = OAuth.OAuthWebRequest(Method.GET, para, string.Empty);
                    counts = ResultToSinaCounts(result);
                }
                catch
                {
                }

            }
            return counts ?? new counts();
        }

        public static List<count> ResultToSinaCounts(string result)
        {
            List<count> counts = null;
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(result)))
            {
                XmlRootAttribute root = new XmlRootAttribute("counts");
                var ser = new XmlSerializer(typeof(counts),root);                
                var sinaCounts = ser.Deserialize(ms) as counts;
                if (sinaCounts != null) counts = sinaCounts;
            }
            return counts;
        }

        public RateLimit ResultToRateLimit(string result)
        {
            RateLimit ratelimit;
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(result)))
            {
                XmlRootAttribute root = new XmlRootAttribute("hash");
                var ser = new XmlSerializer(typeof(RateLimit), root);
                ratelimit = ser.Deserialize(ms) as RateLimit;
            }
            return ratelimit;
        }

        public string Followers()
        {
            return Followers(GetUserId());
        }

        public string Followers(string userId)
        {
            return Followers(userId, 20);
        }

        public string Followers(string userId, int count)
        {
            return Followers(userId, count, -1);
        }

        public string Followers(string userId, int count, int cursor)
        {
            string url = string.Format("http://api.t.sina.com.cn/statuses/followers/{0}.xml?count={1}&cursor={2}", 
                userId, count>=20?count:20, cursor );
            string response = OAuth.OAuthWebRequest(Method.GET, url, null);
            return response;
        }

        public List<SinaUser> FollowersOnList()
        {
            return FollowersOnList(Followers());
        }

        public List<SinaUser> FollowersOnList(string resultstring)
        {
            var xmldoc = new XmlDocument();
            xmldoc.LoadXml( resultstring );
            return GetFollowerUserList(xmldoc);
        }

        private List<SinaUser> GetFollowerUserList(XmlDocument xmlDocument)
        {
            XmlNodeList userNodeListWithLatestStatus = xmlDocument.SelectNodes("/users/user");
            return (from XmlNode userNodeWithStatus in userNodeListWithLatestStatus select GetSinaUser(userNodeWithStatus)).ToList();
        }

        public int GetNextCursor(string resultstring)
        {
            var xmldoc = new XmlDocument();
            xmldoc.LoadXml(resultstring);
            var nextCursorNode = xmldoc.SelectSingleNode("/users/next_cursor");
            int cursorValue = int.Parse(nextCursorNode.InnerText);
            return cursorValue;
        }

        static public string OneLineHeader = @"微博名,粉丝数,关注数,微博数,性别,位置,是否认证,开放私信,微博地址,描述";
        static public string GetSinaUserInOneLine(SinaUser usr)
        {
            string resultformatter = @"{0},{1},{2},{3},{4},{5},{6},{7},http://weibo.com/{8},{9}";
            return string.Format(resultformatter, 
                usr.name, 
                usr.followers_count, usr.friends_count, usr.statuses_count, 
                usr.gender, usr.location,
                usr.verified, usr.allow_all_act_msg, usr.id, 
                usr.description);
        }

        public SinaUser ShowUser(string oid)
        {
            string url = string.Format("http://api.t.sina.com.cn/users/show.xml?user_id={0}",oid);
            string result = OAuth.OAuthWebRequest(Method.GET, url, null);
            var xmldoc = new XmlDocument();
            xmldoc.LoadXml(result);
            return GetSinaUser(xmldoc.SelectSingleNode("/user"));
        }

        /// <summary>
        /// 成为uid的粉丝
        /// </summary>
        /// <param name="uid">用户uid</param>
        /// <returns></returns>
        public string CreateFollow(string uid)
        {
            string url = @"http://api.t.sina.com.cn/friendships/create.xml";
            return OAuth.OAuthWebRequest(Method.POST, url, "user_id=" + uid);
        }

        /// <summary>
        /// 取消对uid的关注
        /// </summary>
        /// <param name="uid">用户uid</param>
        /// <returns></returns>
        public string DestroyFollow(string uid)
        {
            string url = string.Format(@"http://api.t.sina.com.cn/friendships/destroy/{0}.xml",uid);
            return OAuth.OAuthWebRequest(Method.POST, url, string.Empty);
        }

        #region IWeiBee Members

        public WeiBeeType UserType
        {
            get { return WeiBeeType.Sina; }
        }

        public OAuthBase GetOAuth()
        {
            return OAuth;
        }

        public UserBase GetWeiBeeUser()
        {
            string url = "http://api.t.sina.com.cn/account/verify_credentials.xml";
            string result = OAuth.OAuthWebRequest(Method.GET, url, null);
            return WeiBeeCommon.Core.Utility.StringToWeiBeeType(result);
        }

        public DateTime GetResetTime()
        {
            WeiBeeCommon.DataTypes.RateLimit rl = ResultToRateLimit(RateLimitStatus());
            DateTime resettime = StringToDateTime(rl.resettime);
            return resettime;
        }

        public int GetTotalRetweetHourlyLimitNumber()
        {
            return 30;
        }

        public int GetRetweetIntervalNumber()
        {
            return 5;
        }
        /// <summary>
        /// WeiBo.com (maybe Unix) return format to C# DateTime
        /// </summary>
        /// <param name="p">Tue Oct 04 22:00:00 +0800 2011</param>
        /// <returns>DateTime</returns>
        private DateTime StringToDateTime(string p)
        {
            DateTime result;
            if (!DateTime.TryParseExact(p, "ddd MMM dd HH:mm:ss zz00 yyyy", new System.Globalization.CultureInfo("en-US"), System.Globalization.DateTimeStyles.AllowWhiteSpaces, out result))
            {
                result = DateTime.Now;
            }
            return result;
        }
        #endregion
    }
}
